/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package com.comut.crud.dao;

import com.comut.crud.pojo.FindAndSet;
import com.comut.crud.pojo.mobile.TrailerDetail;
import com.comut.crud.pojo.mobile.TrailerInfo;
import com.comut.crud.pojo.trailer.Trailer;
import com.comut.lib.connection.MongoDBConnectionManager;
import com.comut.lib.error.ErrorCodes;
import com.comut.lib.exception.BaseException;
import com.comut.lib.exception.ObjectFoundException;
import com.comut.lib.exception.ObjectNotFoundException;
import java.sql.*;
import java.util.HashMap;
import java.util.logging.Level;
import javax.jms.ConnectionFactory;
import javax.jms.DeliveryMode;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.MessageConsumer;
import javax.jms.MessageProducer;
import javax.jms.Queue;
import javax.jms.Session;
import javax.jms.TextMessage;
import javax.naming.InitialContext;
import org.apache.log4j.Logger;

/**
 *
 * @author asantos
 */
public class TrailerDao {

    private static Logger _logger = Logger.getLogger(TrailerDao.class.getName());
    private Connection _connection;
    private MongoDBConnectionManager _mongodb;

    public TrailerDao(Connection connection) {
        this._connection = connection;
    }

    /**
     * Issue:IFL-1572
     *
     * @param connection
     * @param mongodb
     */
    public TrailerDao(Connection connection, MongoDBConnectionManager mongodb) {
        this._connection = connection;
        this._mongodb = mongodb;
    }

    /**
     *  @author celia calcada
     *  ...
     */
    public ResultSet getTrailer(int id) {

        ResultSet rs = null;
        CallableStatement stmt = null;
        String query = "{call [crud].[usp_getTrailer](?)}";
        try {
            stmt = _connection.prepareCall(query);
            stmt.setInt(1, id);
            rs = stmt.executeQuery();
        } catch (SQLException ex) {
            _logger.error("getTrailer error", ex);
            throw new BaseException(ex, ex.getErrorCode());
        } finally {
        }

        return rs;
    }

    public int createTrailer(Trailer trailer) {

        int toRet = -1;
        ResultSet rs = null;
        CallableStatement stmt = null;
        String query = "{call [crud].[usp_createTrailer](?,?,?,?,?)}";
        try {

            stmt = _connection.prepareCall(query);
            stmt.setLong(1, trailer.getThermo_id());
            stmt.setString(2, trailer.getLicense_plate());
            if (trailer.getInspection_date() == null) {
                stmt.setDate(3, null);
            } else {
                stmt.setDate(3, new java.sql.Date(trailer.getInspection_date().getTime()));
            }


            if (trailer.getInspection_date() == null) {
                stmt.setDate(4, null);
            } else {
                stmt.setDate(4, new java.sql.Date(trailer.getCold_inspection_date().getTime()));
            }

            stmt.setString(5, trailer.getThermograph_certification());
            rs = stmt.executeQuery();

            if (rs.next()) {
                toRet = rs.getInt(1);
            }
        } catch (SQLException ex) {
            _logger.error("createTrailer error", ex);
            if (ex.getErrorCode() == ErrorCodes.OBJECT_FOUND) {
                throw new ObjectFoundException(ex, ex.getErrorCode());
            } else {
                throw new BaseException(ex, ex.getErrorCode());
            }
        } catch (Exception ex) {
            _logger.error("createTrailer error", ex);
            throw new BaseException(ex, ErrorCodes.GENERAL_ERROR);
        } finally {
            if (rs != null) {
                try {
                    rs.close();
                } catch (SQLException ignore) {
                }
            }
            if (stmt != null) {
                try {
                    stmt.close();
                } catch (SQLException ignore) {
                }
            }
        }

        return toRet;
    }

    /**
     * Update the trailer table in SQLServer
     *
     * @param trailer Object with the where clause and the update clause
     */
    public void updateTrailer(FindAndSet trailer) {

        String update = "UPDATE [cmt].[trailer] SET " + trailer.getUpdate(); //DELTA = " + delta
        String where = " WHERE " + trailer.getFind();

        PreparedStatement stmt = null;
        try {
            stmt = _connection.prepareStatement(update + where);
            int updateRows = stmt.executeUpdate();
            if (updateRows == 0) {
                throw new ObjectNotFoundException("The trailer does not exist", ErrorCodes.OBJECT_NOT_FOUND);
            }
        } catch (SQLException ex) {
            _logger.fatal("updateTrailer error", ex);
            throw new BaseException(ex);
        } finally {
            if (stmt != null) {
                try {
                    stmt.close();
                } catch (SQLException ignore) {
                }
            }
        }

    }

    public void deleteTrailer(int id) {
        CallableStatement stmt = null;
        String query = "{call [crud].[usp_deleteTrailer](?)}";
        try {
            stmt = _connection.prepareCall(query);
            stmt.setInt(1, id);
            stmt.execute();
        } catch (SQLException ex) {
            _logger.error("deleteTrailer error", ex);
            if (ex.getErrorCode() == ErrorCodes.OBJECT_NOT_FOUND) {
                throw new ObjectFoundException(ex, ex.getErrorCode());
            } else {
                throw new BaseException(ex, ex.getErrorCode());
            }
        } finally {
            if (stmt != null) {
                try {
                    stmt.close();
                } catch (SQLException ignore) {
                }
            }
        }
    }

    /**
     * Issue: IFL-1572
     * @author celia calcada
     * @param userId
     * @return
     */
    public TrailerInfo[] getTrailersForUserId(int userId) {
        HashMap<Integer, TrailerInfo> trailers = this.getTrailersForUserIdSQL(userId);

        try {
            InitialContext initialContext = new InitialContext();
            ConnectionFactory connectionFactory = (ConnectionFactory) initialContext.lookup("/ConnectionFactory");
            javax.jms.Connection jmsConnection = (javax.jms.Connection) connectionFactory.createConnection();
            jmsConnection.start();

            Session jmsSession = (Session) jmsConnection.createSession(false, Session.AUTO_ACKNOWLEDGE);
            String taskId = com.comut.crud.util.Util.generateUUID();
            int numberSentMessages = sendJMSMessages(initialContext, jmsSession, trailers, taskId);
            waitJMSMessageReplies(initialContext, jmsSession, numberSentMessages, trailers, taskId);

            jmsSession.close();
            jmsConnection.close();
        } catch (Exception ex) {
            java.util.logging.Logger.getLogger(this.getClass().getName()).log(Level.SEVERE, null, ex);
        }

        TrailerInfo[] ret = new TrailerInfo[trailers.values().size()];
        return trailers.values().toArray(ret);
    }

    /**
     * Issue: IFL-1572
     * @author celia calcada
     * @param userId
     * @return
     */
    private HashMap<Integer, TrailerInfo> getTrailersForUserIdSQL(int userId) {
        ResultSet resultSet = null;
        CallableStatement statement = null;
        String query = "{call [crud].[usp_getTrailersByUserId](?)}";
        HashMap<Integer, TrailerInfo> trailers = new HashMap<Integer, TrailerInfo>();

        try {
            statement = _connection.prepareCall(query);
            statement.setInt(1, userId);
            resultSet = statement.executeQuery();

            while (resultSet.next()) {
                TrailerInfo trailerInfo = new TrailerInfo();
                trailerInfo.setId(resultSet.getInt("id"));
                trailerInfo.setProbeId(resultSet.getLong("probeId"));
                trailerInfo.setLicensePlate(resultSet.getString("licensePlate"));
                trailers.put(trailerInfo.getId(), trailerInfo);
            }
        } catch (Exception ex) {
            java.util.logging.Logger.getLogger(this.getClass().getName()).log(Level.SEVERE, null, ex);
        }

        return trailers;
    }

    /**
     * Issue: IFL-1572
     *
     * @param initialContext
     * @param jmsSession
     * @param trailers
     * @param taskId
     * @return
     */
    private int sendJMSMessages(InitialContext initialContext, Session jmsSession, HashMap<Integer, TrailerInfo> trailers, String taskId) {
        int nrMsgs = 0;

        try {
            Queue trailerInfoQueue = (Queue) initialContext.lookup("/queue/TrailerInfoQueue");
            MessageProducer jmsMessageProducer = jmsSession.createProducer(trailerInfoQueue);

            int maxPriority = 9;
            int messageTimeToLiveMilliseconds = 10000;

            for (TrailerInfo trailerInfo : trailers.values()) {
                TextMessage message = jmsSession.createTextMessage(String.format("Trailer id:%d", trailerInfo.getId()));
                message.setIntProperty("trailerId", trailerInfo.getId());
                message.setLongProperty("milliTime", System.currentTimeMillis());
                message.setStringProperty("taskId", taskId);
                jmsMessageProducer.send(message, DeliveryMode.NON_PERSISTENT, maxPriority, messageTimeToLiveMilliseconds);
                nrMsgs++;
            }
            jmsMessageProducer.close();

        } catch (Exception ex) {
            java.util.logging.Logger.getLogger(this.getClass().getName()).log(Level.INFO, "Error sending JMS message TrailerDAO");
        }

        return nrMsgs;
    }

    /**
     * Issue: IFL-1572
     *
     * @param initialContext
     * @param jmsSession
     * @param numberSentMessages
     * @param trailers
     * @param taskId
     */
    private void waitJMSMessageReplies(InitialContext initialContext, Session jmsSession, int numberSentMessages, HashMap<Integer, TrailerInfo> trailers, String taskId) {
        MessageConsumer consumer = null;
        Queue trailerInfoReplyQueue = null;
        String messageSelector = String.format("taskId = '%s' ", taskId);
        java.util.logging.Logger.getLogger(this.getClass().getName()).log(Level.INFO, messageSelector);

        try {
            trailerInfoReplyQueue = (Queue) initialContext.lookup("/queue/TrailerInfoReplyQueue");
            // gets replies for taskId
            consumer = jmsSession.createConsumer(trailerInfoReplyQueue, messageSelector);
            int timeoutReceiveMilliseconds = 15000 / numberSentMessages;

            for (int msgIndex = 0; msgIndex < numberSentMessages; msgIndex++) {
                Message reply = consumer.receive(timeoutReceiveMilliseconds);

                if (reply != null) {
                    int trailerId = reply.getIntProperty("trailerId");
                    Date from = null;

                    if (reply.getLongProperty("from") != -1) {
                        from = new Date(reply.getLongProperty("from"));
                    }

                    Date to = null;
                    if (reply.getLongProperty("to") != -1) {
                        to = new Date(reply.getLongProperty("to"));
                    }

                    int vehicleId = reply.getIntProperty("vehicleId");
                    int gsp = reply.getIntProperty("gsp");
                    int hdg = reply.getIntProperty("hdg");
                    double lat = reply.getDoubleProperty("lat");
                    double lon = reply.getDoubleProperty("lon");
                    String fractal = reply.getStringProperty("fractal");

                    TrailerInfo ti = trailers.get(trailerId);
                    ti.setConnectionDate(from);
                    ti.setDisconnectionDate(to);
                    ti.setVehicleId(vehicleId);
                    ti.setHeading(hdg);
                    ti.setSpeed(gsp);
                    ti.setFractal(fractal);
                    ti.setLatitude(lat);
                    ti.setLongitude(lon);

                    if ((from != null && to == null)) {
                        ti.setState("Connected");
                    } else {
                        ti.setState("Disconnected");
                    }

                }
            }

        } catch (Exception ex) {
            java.util.logging.Logger.getLogger(this.getClass().getName()).log(Level.SEVERE, null, ex);
        } finally {
            if (consumer != null) {
                try {
                    consumer.close();
                } catch (JMSException ex) {
                    java.util.logging.Logger.getLogger(this.getClass().getName()).log(Level.SEVERE, null, ex);
                }
            }
        }
    }

    /**
     * Issue: IFL-1573
     * @author celia calcada
     * @param userId
     * @param trailerId
     * @return
     */
    public ResultSet getTrailerDetail(int userId, int trailerId) {
        ResultSet resultSet = null;
        CallableStatement statement = null;
        String query = "{call [crud].[usp_getTrailerDetail](?,?)}";
        TrailerDetail ret = null;
        
        try {
            statement = _connection.prepareCall(query);
            statement.setInt(1, userId);
            statement.setInt(2, trailerId);
            resultSet = statement.executeQuery();
        } catch (Exception ex) {
            java.util.logging.Logger.getLogger(this.getClass().getName()).log(Level.SEVERE, null, ex);
        }
        
        return resultSet;
    }
}
